/*
This is part of Screenbar, which is based in part on the Litestep
shell source code.

Copyright (C) 2008-2009 Jim Babcock
Copyright (C) 1997-2009 The LiteStep Development Team

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#include "stdafx.h"
#include <CommCtrl.h>

static const int litestepMessageTypes[] = {
	LM_GETREVID,
	LM_SWITCHTON,
	LM_WINDOWACTIVATED,
	LM_LISTDESKTOPS,
	LM_GETDESKTOPOF,
	LM_WINDOWCREATED,
	LM_WINDOWDESTROYED,
	LM_GETMINRECT,
	LM_REDRAW,
	0
};

//=========================================================
// Message handling
//=========================================================
static bool eventHandlersRegistered = false;

void ScreenbarPanel::registerEventHandlers()
{
	if(!eventHandlersRegistered)
	{
		// Set a timer to update the VWM display and follow the mouse
		if(settings->pollInterval)
			SetTimer(window, TIMER_POLL, settings->pollInterval, NULL);
		if(settings->hoverTrackingInterval)
			SetTimer(window, TIMER_HOVERTRACK, settings->hoverTrackingInterval, NULL);
		
		// Register LiteStep messages
		SendMessage(GetLitestepWnd(), LM_REGISTERMESSAGE, (WPARAM)window, (LPARAM)litestepMessageTypes);
		
		isEventHandler = true;
		eventHandlersRegistered = true;
	}
}

void ScreenbarPanel::unregisterEventHandlers()
{
	if(isEventHandler)
	{
		SendMessage(GetLitestepWnd(), LM_UNREGISTERMESSAGE, (WPARAM)window, (LPARAM)litestepMessageTypes);
		
		isEventHandler = false;
		eventHandlersRegistered = false;
	}
}

LRESULT globalWindowProc(HWND window, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
		case WM_SYSCOMMAND: {
			if(wParam == SC_CLOSE) {
				PostMessage(getDesktopWindow(), WM_SYSCOMMAND, wParam, lParam);
				return 0;
			} else {
				return DefWindowProc(window, uMsg, wParam, lParam);
			}
		}
		
		case WM_TIMER: {
			int timerID = (int)wParam;
			if(timerID == TIMER_HOVERTRACK)
			{
				updateHover();
			}
			else if(timerID == TIMER_POLL)
			{
				if(settings->trackProcesses)
					processTracker->update();
				windowTracker->update();
				snapshotManager->updateSnapshots();
				for(unsigned ii=0; ii<panels.size(); ii++)
					panels[ii]->checkForUpdates();
				updateToolTip();
			}
			return 0;
		}
			
		case WM_DROPFILES:
			//TODO
			return 0;
		
		case WM_DISPLAYCHANGE:
			monitors->refresh();
			refreshPanels();
			return 0;
			
		case LM_REFRESH:
			settings->refresh();
			refreshPanels();
			return 0;
		
		case WM_KEYDOWN:
		case WM_KEYUP:
		case WM_HOTKEY: {
			PostMessage(getDesktopWindow(), uMsg, wParam, lParam);
			return 0;
		}
		
		case LM_GETREVID: {
			char *buf = (char*)(lParam);
			sprintf(buf, VERSION_STRING);
			return strlen(buf);
		}
			
		case LM_SWITCHTON:
			return onSwitchToN(wParam, lParam);
			
		case LM_LISTDESKTOPS:
			return onListDesktops(wParam, lParam);
			
		case LM_GETDESKTOPOF:
			return onGetDesktopOf(wParam, lParam);
			
		case LM_REDRAW: {
			HWND handle = (HWND)wParam;
			WindowData *window = windowTracker->getWindow(handle);
			
			if(window)
			{
				if(lParam != 0) {
					if(!window->isFocusedTask())
						window->flashing = true;
				} else {
					window->updateIcon();
				}
			}
			
			// Indicates that a window changed title. (Possibly to what it was
			// before; for example, focusing Vis. Studio generates 2-6 copies
			// of this message). Check for updates.
			// (Which window is given by wParam, but our update-checking
			// interface can't really make use of this information.)
			for(unsigned ii=0; ii<panels.size(); ii++)
				panels[ii]->checkForUpdates();
			return 0;
		}
		
		case LM_WINDOWCREATED: {
			HWND handle = (HWND)wParam;
			windowTracker->windowCreated(handle);
			if(windowTracker->checkForChange())
				updateLayouts();
			return 0;
		}
			
		case LM_WINDOWDESTROYED: {
			HWND handle = (HWND)wParam;
			windowTracker->windowDestroyed(handle);
			if(windowTracker->checkForChange())
				updateLayouts();
			return 0;
		}
			
		case LM_WINDOWACTIVATED: {
			HWND handle = (HWND)wParam;
			
			windowTracker->windowActivated(handle);
			
			WindowData *foregroundWindow = windowTracker->getForegroundWindow();
			if(foregroundWindow)
			{
				windowTracker->lastFocus = NULL;
				foregroundWindow->flashing = false;
				
				if(foregroundWindow->desk && !foregroundWindow->desk->isVisible())
				{
					Monitor *monitor = findSingleMonitor(settings->altTabMonitor);
					if(!monitor) monitor = monitors->getPrimaryMonitor();
					
					if(settings->switchOnFocus)
						vwm->switchDesk(foregroundWindow->desk, monitor, true);
				}
			}
			
			if(windowTracker->checkForChange())
				updateLayouts();
			
			return 0;
		}
		
		case LM_GETMINRECT:
		{
			// There are several calling conventions this might be using,
			// because of a bug in older LS versions (0.24.7). If there's
			// both a wParam and an lParam, they're the window and its rect;
			// if there's no lParam, then wParam is an LPSHELLHOOKINFO (which
			// contains both).
			HWND windowHandle;
			LPPOINTS rect;
			
			if(!lParam) {
				LPSHELLHOOKINFO shellHookInfo = (LPSHELLHOOKINFO)wParam;
				windowHandle = shellHookInfo->hwnd;
				rect = (LPPOINTS)&shellHookInfo->rc;
			} else {
				windowHandle = (HWND)wParam;
				rect = (LPPOINTS)lParam;
			}
			
			
			// Find any panels on the same monitor as this window
			VirtualDesktop *desk = vwm->getDeskFromWnd(windowHandle);
			if(!desk) return 0;
			if(!desk->isVisible()) return 0;
			Monitor *monitor = desk->getMonitor();
			vector<ScreenbarPanel*> panels = monitor->getPanelsOnMonitor();
			
			// Find a layout element with the <prefix>IsMinimizeTarget
			// attribute set on one of those panels
			vector<LayoutCacheNode*> elements;
			for(unsigned ii=0; ii<panels.size(); ii++)
			{
				Layout *layout = panels[ii]->getLayout();
				if(layout) layout->traverse(elements);
			}
			for(unsigned ii=0; ii<elements.size(); ii++)
			{
				if(!elements[ii]->element->isMinimizeTarget)
					continue;
				if(elements[ii]->context.taskWindow != windowHandle)
					continue;
				
				ScreenbarWindow *window = elements[ii]->context.window;
				Rect boundingRect = elements[ii]->boundingRect;
				Point panelPos = window->getTopLeft();
				boundingRect.left += panelPos.x;
				boundingRect.top += panelPos.y;
				rect[0].x = boundingRect.left;
				rect[0].y = boundingRect.top;
				rect[1].x = boundingRect.getRight();
				rect[1].y = boundingRect.getBottom();
				return 1;
			}
			
			return 0;
		}
		
		default:
			return DefWindowProc(window, uMsg, wParam, lParam);
	}
}

LRESULT onSwitchToN(WPARAM wParam, LPARAM lParam)
{
	int index = (int)wParam;
	
	VirtualDesktop *newDesk = vwm->getDesk(index);
	if(newDesk) {
		vwm->switchDesk(newDesk, NULL, true);
		return TRUE;
	} else {
		return FALSE;
	}
}

LRESULT onListDesktops(WPARAM wParam, LPARAM lParam)
{
	LSDESKTOPINFO deskInfo;

	for(int ii=0; ii<vwm->numDesktops(); ii++)
	{
		VirtualDesktop *desk = vwm->getDesk(ii);
		
		deskInfo.size = sizeof(LSDESKTOPINFO);
		deskInfo.icon = 0;
		deskInfo.isCurrent = desk->isVisible()?1:0;
		deskInfo.number = ii;
		
		string deskname = desk->getName();
		strcpy(deskInfo.name, deskname.c_str());

		SendMessage((HWND)wParam, LM_DESKTOPINFO, 0, (LPARAM)&deskInfo);
	}

	return TRUE;
}

LRESULT onGetDesktopOf(WPARAM wParam, LPARAM lParam)
{
	VirtualDesktop *desk = vwm->getDeskFromWnd((HWND)wParam);
	return desk->getIndex();
}
